'use strict'

const http = require('http')
const fs = require('fs');
const { writer } = require('repl');

const fsp = fs.promises
/**----------------解析body数据------------ */

/**
 * Content-Disposition:form-data; name="file"; filename="xxx"
 * Content-Type:text/plain
 * @param {string} headerData 
 */

function parseBodyHeaders(headerData) {
    let hlist = headerData.split('\r\n').filter(p => p.length > 0)//hlist=["Content-Disposition:form-data; name="file"; filename="xxx"",
    //"Content-Type:text/plain"]

    //解析content-disposition=>hlist[0]
    //按照切分数组，选出从第二个元素开始的数组
    //从索引为1开始到最后选出来，把第一个下标为0的去除掉
    let csplit = hlist[0].split('; ').slice(1) //csplit=["name='file'","filename='xxx'"]

    let filename
    let name
    for (let a of csplit) {
        if (a.indexOf('filename=') === 0) {
            filename = a.substring(a.indexOf('=') + 2, a.length - 1)
        } else {
            name = a.substring(a.indexOf('=') + 2, a.length - 1)
        }
    }

    let headers = {}
    let ind = 0
    let k
    for (let h of hlist) {
        ind = h.indexOf(':')
        k = h.substring(0, ind).toLowerCase()
        headers[k] = h.substring(ind + 1).trim()  //trim实现去掉字符串两端的空白
    }
    return {
        filename,
        name,
        headers,
        contentType: headers['content-type'] || ''
    }

}

function parseBody(bodyData, headers) {//headers目的：只希望拿到content-type
    let ctype = headers['content-type']

    let bdy = ctype.substring(ctype.indexOf('=') + 1)

    let crlf_bdy = `\r\n--${bdy}`

    let header_end_index = bodyData.indexOf('\r\n\r\n', crlf_bdy.length);

    let headerData = bodyData.toString('utf8', crlf_bdy.length, header_end_index);

    //解析文件头信息
    let hd = parseBodyHeaders(headerData)

    let file = {
        start: header_end_index + 4,
        end: bodyData.indexOf(crlf_bdy, header_end_index),
        ...hd
    }

    file.length = file.end - file.start

    return file
}
//解析文件扩展名
function parseExtName(filename){
    if(filename.length<2){
        return ''
    }

    let nsplit = filename.split('.').filter(p=>p.length>0)
    if(nsplit.length<2) return ''
    return `.${nsplit[nsplit.length-1]}`
}
/**----------------解析body数据 -END------------ */


let html = `
    <!DOCTYPE html>
    <html>
        <head>
            <title>First Http</title>
        </head>
        <body>
            你好。
        </body>
    </html>
`

let routerTable = {
    GET: {
        '/': async (ctx) => {
            response.setHeader('content-type', 'text/html;charset=utf-8')
            ctx.end(html)
        },
        '/test': async (ctx) => {
            let status = parseInt(Math.random() * 200) + 199
            ctx.statusCode = status
            ctx.end('test end')
        },
        '/upload': async (ctx) => {
            let stm = fs.createReadStream('./upload.html')
            stm.pipe(ctx.response, { end: false })

            stm.on('end', () => {
                ctx.end()
            })
        }
    },
    POST: {
        '/data': async (ctx) => {
            let file = parseBody(ctx.rawBody, ctx.headers)
            console.log(file)

            if (!ctx.rawBody) {
                ctx.end();
                return;
            }
            let fname = `${Date.now()}--${Math.random()}`+`${parseExtName(file.filename)}`
            let fh = await fsp.open(fname, 'w+')
            let fret = await fh.write(ctx.rawBody, file.start, file.length, 0)

            //关闭打开的文件
            fh.close();

            ctx.end(`${fret.bytesWritten}`)
        }
    }
}

http.createServer(async (request, response) => {
    let rm = routerTable[request.method]
    let uspLit = request.url.split('?')

    let pathname = uspLit[0]
    let querystring = uspLit[1] || ''

    if (!rm || !rm[pathname]) {
        response.statusCode = 404
        response.end('page not found')
        return
    }
    //ctx是context的缩写，意为请求上下文，就是一个打包了各种数据和方法的对象
    let ctx = {
        method: request.method,
        path: pathname,
        search: querystring,
        headers: request.headers,
        request: request,
        response: response,
        end: (data, encoding = 'utf8') => {
            response.end(data, encoding)
        },
        write: (data, encoding = 'utf8') => {
            response.write(data, encoding)
        },
        totalLength: 0,
        rawBody: null,
        bufferList: []
    }

    await new Promise((rv, rj) => {
        if ('PD'.indexOf(request.method[0]) >= 0) {
            request.on('data', chunk => {
                ctx.bufferList.push(chunk)
                ctx.totalLength += chunk.length
            })
        } else {
            request.on('data', data => {
                response.statusCode = 400
                response.end('bad request', () => {
                    request.destroy()  //会触发close事件，但不会触发end事件
                })
            })
        }
        let resolved = false
        request.on('end', () => {
            resolved = true;
            rv();
        })
        request.on('close', () => {
            (!resolved) && rv();

        })
    })
    // GET  HEAD  TRACE   OPTIONS
    // 必须要发送数据:POST  PUT  PATCH        发送数据可选：DELETE
    if (!response.writable || response.writableEnded) {
        return
    }

    if (ctx.bufferList.length > 0) {
        ctx.rawBody = Buffer.concat(ctx.bufferList, ctx.totalLength)
        ctx.bufferList = []
    }
    rm[pathname](ctx)
}).listen(3456)